package com.wonderful.map.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.CornerPathEffect
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
import com.wonderful.image.utils.PointUtil
import com.wonderful.map.R
import com.wonderful.map.bean.CirclesBean
import com.wonderful.map.bean.GridPositionBean
import com.wonderful.map.bean.VirtualObstacleBean
import com.wonderful.map.constant.MapConstant
import com.wonderful.map.utils.DensityUtil
import kotlin.math.abs

/**
 * Desc: base 虚拟 view
 * @Author: wonderful
 * @Time: 2023/5/30 15:57
 */
open class BaseVirtualView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {

    protected val mPolylinePaint = Paint()
    protected val mPolygonPaint = Paint()
    protected val mCirclePaint = Paint()

    private val mPolygonPath = Path()

    private val mNewPolylineData = mutableListOf<GridPositionBean>()// 新建线段
    private var mNewPolylineLastData = GridPositionBean()           // 新建线段最后一个点

    private val mNewPolygonData = mutableListOf<GridPositionBean>() // 新建多边形
    private var mNewPolygonLastData = GridPositionBean()            // 新建多边形最后一个点

    private var mNewCircleData: CirclesBean? = null                 // 新建圆

    private val mClickPolygon = mutableListOf<GridPositionBean>()   // 被点击的多边形
    private val mClickPolyline = mutableListOf<GridPositionBean>()  // 被点击的线段
    private var mClickCircle: CirclesBean? = null                   // 被点击的圆


    private val mVirtualObstacleList = mutableListOf<VirtualObstacleBean>() // 数据

    private val mTranslate: PointF = PointF()
    private var mScale: Float = 1f

    private var mClickType = -1 // 当前点击的类型(-1为未点击)
    private var isCanClick = false  // 是否可点击
    private var isEditMode = false  // 是否是编辑模式
    private var mObstacleType = MapConstant.EditType.VIRTUAL_WALL // 虚拟障碍物类别

    private var mClickListener: ((isClicked: Boolean) -> Unit)? = null // 点击监听

    protected val colorDefault = ContextCompat.getColor(context, R.color.color_default)
    protected val colorClicked = ContextCompat.getColor(context, R.color.color_clicked)
    private val clickOffsetWidth = DensityUtil.dp2px(context, 10f).toFloat() // 点击偏离误差大小

    init {
        val radius = 10f // 多边形圆角
        val corEffect = CornerPathEffect(radius)

        mPolylinePaint.apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            color = colorDefault
            strokeWidth = DensityUtil.dp2px(context, 2f).toFloat()
            pathEffect = corEffect
        }

        mPolygonPaint.apply {
            isAntiAlias = true
            style = Paint.Style.FILL
            color = colorDefault
            strokeWidth = DensityUtil.dp2px(context, 2f).toFloat()
            pathEffect = corEffect
        }

        mCirclePaint.apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            color = colorDefault
            strokeWidth = DensityUtil.dp2px(context, 2f).toFloat()
        }
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.let {
            drawPolyline(it)
            drawPolygon(it)
            drawCircle(it)
        }
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        // 处于编辑状态不响应点击事件
        if (isEditMode || !isCanClick) return super.onTouchEvent(event)
        if (event?.action == MotionEvent.ACTION_DOWN) {
            resetClick()
            val pointF = PointUtil.viewToSourceCoordinate(event.x, event.y, mTranslate, mScale)
            val clicked = checkClick(pointF)
            mClickListener?.invoke(clicked)
            if (clicked) return true
        }
        return super.onTouchEvent(event)
    }

    /**
     * 绘制线段
     */
    private fun drawPolyline(canvas: Canvas) {
        updatePaintStyle(false, mObstacleType)
        drawPolyline(mNewPolylineData, canvas)
        for (virtualObstacleBean in mVirtualObstacleList) {
            for (points in virtualObstacleBean.obstacles.polylines) {
                updatePaintStyle(points == mClickPolyline, virtualObstacleBean.obstaclesType)
                drawPolyline(points, canvas)
            }
        }
    }

    protected fun drawPolyline(points: List<GridPositionBean>, canvas: Canvas) {
        for (i in 0 until points.size - 1) {
            val p1 = points[i]
            val p2 = points[i + 1]
            val pointF = PointUtil.sourceToViewCoordinate(p1.x, p1.y, mTranslate, mScale)
            val nextPointF = PointUtil.sourceToViewCoordinate(p2.x, p2.y, mTranslate, mScale)
            canvas.drawLine(pointF.x, pointF.y, nextPointF.x, nextPointF.y, mPolylinePaint)
        }
    }

    /**
     * 绘制多边形
     */
    private fun drawPolygon(canvas: Canvas) {
        updatePaintStyle(false, mObstacleType)
        drawPolygon(mNewPolygonData, canvas)
        for (virtualObstacleBean in mVirtualObstacleList) {
            for (points in virtualObstacleBean.obstacles.polygons) {
                updatePaintStyle(points == mClickPolygon, virtualObstacleBean.obstaclesType)
                drawPolygon(points, canvas)
            }
        }
    }

    private fun drawPolygon(points: List<GridPositionBean>, canvas: Canvas) {
        if (points.isNotEmpty()) {
            mPolygonPath.reset()
            for (i in points.indices) {
                val point = points[i]
                val pointF = PointUtil.sourceToViewCoordinate(point.x, point.y, mTranslate, mScale)
                if (i == 0) {
                    mPolygonPath.moveTo(pointF.x, pointF.y)
                } else {
                    mPolygonPath.lineTo(pointF.x, pointF.y)
                }
            }
            mPolygonPath.close()
            canvas.drawPath(mPolygonPath, mPolygonPaint)
            if (points.size <= 3) {
                for (i in 0 until points.size - 1) {
                    val p1 = points[i]
                    val p2 = points[i + 1]
                    val pointF = PointUtil.sourceToViewCoordinate(p1.x, p1.y, mTranslate, mScale)
                    val nextPointF =
                        PointUtil.sourceToViewCoordinate(p2.x, p2.y, mTranslate, mScale)
                    canvas.drawLine(pointF.x, pointF.y, nextPointF.x, nextPointF.y, mPolygonPaint)
                }
            }
        }
    }

    /**
     * 绘制圆形
     */
    private fun drawCircle(canvas: Canvas) {
        updatePaintStyle(false, mObstacleType)
        mNewCircleData?.let { drawCircle(it, canvas) }
        for (virtualObstacleBean in mVirtualObstacleList) {
            for (circle in virtualObstacleBean.obstacles.circles) {
                updatePaintStyle(circle == mClickCircle, virtualObstacleBean.obstaclesType)
                drawCircle(circle, canvas)
            }
        }
    }

    private fun drawCircle(circlesBean: CirclesBean, canvas: Canvas) {
        val center = circlesBean.center
        val pointF = PointUtil.sourceToViewCoordinate(center.x, center.y, mTranslate, mScale)
        val radius = DensityUtil.dp2px(context, circlesBean.radius) * mScale
        canvas.drawCircle(pointF.x, pointF.y, radius, mCirclePaint)
    }

    /**
     * 更新画笔样式
     *
     * @param isClicked 是否被点击
     * @param type 虚拟障碍物类别
     */
    protected open fun updatePaintStyle(isClicked: Boolean, type: Int = -1) {
        if (isClicked) {
            mCirclePaint.color = colorClicked
            mPolygonPaint.color = colorClicked
            mPolylinePaint.color = colorClicked
            return
        }
        mCirclePaint.color = colorDefault
        mPolygonPaint.color = colorDefault
        mPolylinePaint.color = colorDefault
    }

    /**
     * 重置点击状态
     */
    private fun resetClick() {
        mClickPolyline.clear()
        mClickPolygon.clear()
        mClickCircle = null
        mClickType = -1
        mClickListener?.invoke(false)
        invalidate()
    }

    /**
     * 重置操作状态
     */
    private fun resetOperate() {
        mNewPolygonData.clear()
        mNewPolylineData.clear()
        mNewCircleData = null
        isEditMode = false
        invalidate()
    }

    /**
     * 检查点击情况
     */
    private fun checkClick(pointF: PointF): Boolean {
        for (virtualObstacleBean in mVirtualObstacleList) {
            mClickType = virtualObstacleBean.obstaclesType
            // 多线段点击事件判断
            for (polyline in virtualObstacleBean.obstacles.polylines) {
                for (i in 0 until polyline.size - 1) {
                    val p1 = polyline[i]
                    val p2 = polyline[i + 1]
                    val isToLine = PointUtil.checkPointToLine(
                        p1.x, p1.y, p2.x, p2.y, pointF.x, pointF.y, clickOffsetWidth
                    )
                    if (isToLine) {
                        //Log.e("VirtualCleanView", "检测到多线段点击")
                        mClickPolyline.addAll(polyline)
                        invalidate()
                        return true
                    }
                }
            }

            // 圆点击事件判断
            for (circle in virtualObstacleBean.obstacles.circles) {
                val isInCircle = isPointInCircle(pointF, circle)
                if (isInCircle) {
                    //Log.e("VirtualCleanView", "检测到圆点击")
                    mClickCircle = circle
                    invalidate()
                    return true
                }
            }

            // 多边形点击事件判断
            for (polygon in virtualObstacleBean.obstacles.polygons) {
                // 内部判断
                val isInPolygon = isPointInPolygon(pointF, polygon)
                if (isInPolygon) {
                    //Log.e("VirtualCleanView", "检测到多边形点击")
                    mClickPolygon.addAll(polygon)
                    invalidate()
                    return true
                }
                // 边界判断
                for (i in 0 until polygon.size - 1) {
                    val p1 = polygon[i]
                    val p2 = polygon[i + 1]
                    val isToLine = PointUtil.checkPointToLine(
                        p1.x, p1.y, p2.x, p2.y, pointF.x, pointF.y, clickOffsetWidth
                    )
                    if (isToLine) {
                        //Log.e("VirtualCleanView", "检测到多线段点击")
                        mClickPolygon.addAll(polygon)
                        invalidate()
                        return true
                    }
                }
            }
        }

        mClickType = -1
        return false
    }

    /**
     * 判断点是否在圆上
     */
    private fun isPointInCircle(pointF: PointF, circlesBean: CirclesBean): Boolean {
        val x = circlesBean.center.x
        val y = circlesBean.center.y
        val radius = circlesBean.radius
        val distance = PointUtil.getDistance(x, y, pointF.x, pointF.y)
        // 空心圆点击事件判断
        val strokeWidth = mCirclePaint.strokeWidth
        return (abs(distance) >= radius - strokeWidth / 2 - clickOffsetWidth) && (abs(distance) <= radius + strokeWidth / 2 + clickOffsetWidth * 2)
    }

    /**
     * 判断点是否在多边形上
     *
     * @param polygon 多边形的各个顶点坐标（首末点可以不一致）
     */
    private fun isPointInPolygon(pointF: PointF, polygon: List<GridPositionBean>): Boolean {
        var crossNum = 0
        for (i in polygon.indices) {
            val p1 = polygon[i]
            val p2 = polygon[(i + 1) % polygon.size]
            // 求解 y=p.y 与 p1p2 的交点
            if (p1.y == p2.y) continue // p1p2 与 getY()=p0.getY() 平行
            if (pointF.y < p1.y.coerceAtMost(p2.y)) continue // 交点在p1p2延长线上
            if (pointF.y >= p1.y.coerceAtLeast(p2.y)) continue // 交点在p1p2延长线上
            // 求交点的 X 坐标
            val x = (pointF.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x
            if (x > pointF.x) crossNum++ // 只统计单边交点
        }
        return (crossNum % 2 == 1) // 单边交点为偶数，点在多边形之外
    }

    /**
     * 设置虚拟障碍物类别
     */
    fun setObstacleType(type: Int) {
        mObstacleType = type
    }

    /**
     * 设置是否是编辑模式
     */
    fun setEditMode(isEdit: Boolean) {
        isEditMode = isEdit
    }

    /**
     * 设置点击监听
     */
    fun setClickListener(listener: ((isClicked: Boolean) -> Unit)) {
        mClickListener = listener
    }

    /**
     * 添加多边形点
     */
    fun addNewPolygonPoint(x: Float, y: Float) {
        mNewPolygonLastData = GridPositionBean(x, y)
        if (mNewPolygonData.isEmpty()) {
            val previousPoint = GridPositionBean(x, y)
            mNewPolygonData.add(previousPoint)
        }
        mNewPolygonData.add(mNewPolygonLastData)
        invalidate()
    }

    /**
     * 设置(更新)多边形最后一个坐标值
     */
    fun setNewPolygonLastPoint(x: Float, y: Float) {
        mNewPolygonLastData.x = x
        mNewPolygonLastData.y = y
        invalidate()
    }

    /**
     * 添加多线段点
     */
    fun addNewPolylinePoint(x: Float, y: Float) {
        mNewPolylineLastData = GridPositionBean(x, y)
        if (mNewPolylineData.isEmpty()) {
            val previousPoint = GridPositionBean(x, y)
            mNewPolylineData.add(previousPoint)
        }
        mNewPolylineData.add(mNewPolylineLastData)
    }

    /**
     * 更新多线段最后一个点坐标
     */
    fun setNewPolylineLastPoint(x: Float, y: Float) {
        mNewPolylineLastData.x = x
        mNewPolylineLastData.y = y
        invalidate()
    }

    /**
     * 添加圆
     */
    fun addNewCircle(x: Float, y: Float) {
        if (mNewCircleData == null) mNewCircleData = CirclesBean()
        mNewCircleData?.apply {
            center = GridPositionBean(x, y)
            radius = 0f
        }
    }

    /**
     * 设置圆半径
     */
    fun setNewCircleLastRadius(x: Float, y: Float) {
        mNewCircleData?.let {
            val r = PointUtil.getDistance(it.center.x, it.center.y, x, y)
            it.radius = r.toFloat()
            invalidate()
        }
    }

    /**
     * 撤销
     */
    fun undo() {
        var isEdit = false
        if (mNewCircleData != null) mNewCircleData = null

        if (mNewPolygonData.isNotEmpty()) {
            mNewPolygonData.removeAt(mNewPolygonData.size - 2)
            isEdit = mNewPolygonData.size > 1
        }

        if (mNewPolylineData.isNotEmpty()) {
            mNewPolylineData.removeAt(mNewPolygonData.size - 2)
            isEdit = mNewPolylineData.size > 1
        }

        if (!isEdit) {
            mNewPolygonData.clear()
            mNewPolylineData.clear()
            isEditMode = false
        }
        invalidate()
    }

    /**
     * 完成
     */
    fun finish(obstacleType: Int) {
        var tBeanIndex = -1
        for (i in mVirtualObstacleList.indices) {
            if (mVirtualObstacleList[i].obstaclesType == obstacleType) {
                tBeanIndex = i
                break
            }
        }
        if (tBeanIndex == -1) {
            val tBean = VirtualObstacleBean().apply {
                obstaclesType = obstacleType
                obstacles.apply {
                    if (mNewPolygonData.isNotEmpty()) polygons.add(mNewPolygonData)
                    if (mNewPolylineData.isNotEmpty()) polylines.add(mNewPolylineData)
                    mNewCircleData?.let { circles.add(it) }
                }
            }
            mVirtualObstacleList.add(tBean)
        } else {
            mVirtualObstacleList[tBeanIndex].obstacles.apply {
                if (mNewPolygonData.isNotEmpty()) polygons.add(mNewPolygonData)
                if (mNewPolylineData.isNotEmpty()) polylines.add(mNewPolylineData)
                mNewCircleData?.let { circles.add(it) }
            }
        }
        resetOperate()
    }

    /**
     * 取消
     */
    fun cancel() {
        resetOperate()
    }

    /**
     * 删除
     */
    fun delete() {
        var isDelete = false
        if (mClickType != -1) {
            for (virtualObstacleBean in mVirtualObstacleList) {
                if (virtualObstacleBean.obstaclesType == mClickType) {
                    virtualObstacleBean.obstacles.let {
                        if (mClickPolygon.isNotEmpty()) {
                            it.polygons.remove(mClickPolygon)
                            isDelete = true
                        }
                        if (mClickPolyline.isNotEmpty()) {
                            it.polylines.remove(mClickPolyline)
                            isDelete = true
                        }
                        if (mClickCircle != null) {
                            it.circles.remove(mClickCircle)
                            isDelete = true
                        }
                    }
                    break
                }
            }
            if (!isDelete) Log.e("VirtualView", "delete type is error")
        }
        updatePaintStyle(false)
        resetClick()
    }

    /**
     * 设置是否可点击
     */
    fun setCanClick(isCanClick: Boolean) {
        this.isCanClick = isCanClick
    }

    /**
     * 添加 VirtualObstacleBean
     */
    fun addVirtualObstacle(
        virtualObstacleBean: VirtualObstacleBean,
        vTranslate: PointF,
        scale: Float
    ) {
        var hasBean = false
        for (tBean in mVirtualObstacleList) {
            if (tBean.obstaclesType == virtualObstacleBean.obstaclesType) {
                hasBean = true
                tBean.obstacles.let {
                    it.polygons.addAll(virtualObstacleBean.obstacles.polygons)
                    it.polylines.addAll(virtualObstacleBean.obstacles.polylines)
                    it.circles.addAll(virtualObstacleBean.obstacles.circles)
                }
                break
            }
        }
        if (!hasBean) {
            mVirtualObstacleList.add(virtualObstacleBean)
        }
        refreshView(vTranslate, scale)
    }

    /**
     * 设置 List<VirtualObstacleBean>
     */
    fun setVirtualObstacles(
        virtualObstacles: List<VirtualObstacleBean>,
        vTranslate: PointF,
        scale: Float
    ) {
        mVirtualObstacleList.clear()
        mVirtualObstacleList.addAll(virtualObstacles)
        refreshView(vTranslate, scale)
    }

    /**
     * 刷新
     */
    fun refreshView(vTranslate: PointF, scale: Float) {
        mTranslate.set(vTranslate)
        mScale = scale
        invalidate()
    }

    /**
     * 清空数据
     */
    open fun clear() {
        mVirtualObstacleList.clear()
    }

    fun getVirtualObstacles() = mVirtualObstacleList
}